package away3d.entities ;


import away3d.bounds.BoundingSphere ;
import away3d.bounds.BoundingVolumeBase ;
import away3d.cameras.Camera3D ;
import away3d.animators.data.AnimationBase ;
import away3d.animators.data.AnimationStateBase ;
import away3d.animators.data.NullAnimation ;
import away3d.core.base.IRenderable ;
import away3d.core.base.SubGeometry ;
import away3d.materials.MaterialBase ;
import away3d.core.partition.EntityNode ;
import away3d.core.partition.RenderableNode ;
import flash.display3D.Context3D ;
import flash.display3D.IndexBuffer3D ;
import flash.display3D.VertexBuffer3D ;
import flash.geom.Matrix3D ;
import flash.geom.Vector3D ;



/**
 * Sprite3D is a 3D billboard, a renderable rectangular area that always faces the camera.
 *
 * todo: mvp generation can probably be optimized
 */
class Sprite3D extends Entity implements IRenderable
{
    
    
    private static var _geometry        : SubGeometry ;
    private static var _nullAnimation   : NullAnimation ;
    private var _mouseDetails           : Bool ;
    private var _material               : MaterialBase ;
    private var _animationState         : AnimationStateBase ;
    private var _spriteMatrix           : Matrix3D ;
    private var _width                  : Float ;
    private var _height                 : Float ;
    private var _shadowCaster           : Bool ;
    
    
    
    public function Sprite3D( material_ : MaterialBase, width : Float, height : Float )
    {
        
        _shadowCaster = false ;
        super() ;
        
        if( _nullAnimation == null )
        {
            
            _nullAnimation ||= new NullAnimation() ;
            
        }
        
        material        = material_ ;
        _width          = width ;
        _height         = height ;
        _spriteMatrix   = new Matrix3D() ;
        
        if( !_geometry )
        {
            
            _geometry = new SubGeometry() ;
            _geometry.updateVertexData(         Vector( [-.5, .5, .0, .5, .5, .0, .5, -.5, .0, -.5, -.5, .0] )          ) ;
            _geometry.updateUVData(             Vector( [.0, .0, 1.0, .0, 1.0, 1.0, .0, 1.0] )                          ) ;
            _geometry.updateIndexData(          Vector( [0, 1, 2, 0, 2, 3] )                                            ) ;
            _geometry.updateVertexTangentData(  Vector( [1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0] )  ) ;
            _geometry.updateVertexNormalData(   Vector( [.0, .0, -1.0, .0, .0, -1.0, .0, .0, -1.0, .0, .0, -1.0] )      ) ;
            
        }
        
    }
    
    
    private var width( get_width, set_width ) : Float ;
    
    
    private function get_width() : Float
    {
        
        return _width ;
        
    }
    
    
    private function set_width( value : Float ) : Float
    {
        
        if( _width == value ) return value ;
        _width = value ;
        invalidateTransform() ;
        return value ;
        
    }
    
    
    public var height( get_height, set_height ) : Float ;
    
    
    private function get_height() : Float
    {
        
        return _height ;
        
    }
    
    
    private function set_height( value : Float ) : Float
    {
        
        if( _height == value ) return value ;
        _height = value ;
        invalidateTransform() ;
        return value ;
        
    }
    
    
    /*override public function lookAt( target : Vector3D, upAxis : Vector3D = null )
    {
        super.lookAt( target, upAxis ) ;
        _transform.appendScale( _width, _height, 1 ) ;
    }*/
    
    
    public var mouseDetails( get_mouseDetails, set_mouseDetails ) : Bool ;
    
    
    private function get_mouseDetails() : Bool
    {
        
        return _mouseDetails ;
        
    }
    
    
    private function set_mouseDetails( value : Bool ) : Bool
    {
        
        _mouseDetails = value ;
        return value ;
        
    }
    
    
    public function getVertexBuffer( context : Context3D, contextIndex : uint ) : VertexBuffer3D
    {
        
        return _geometry.getVertexBuffer( context, contextIndex ) ;
        
    }
    
    
    public function getUVBuffer( context : Context3D, contextIndex : uint ) : VertexBuffer3D
    {
        
        return _geometry.getUVBuffer( context, contextIndex ) ;
        
    }
    
    
    public function getVertexNormalBuffer( context : Context3D, contextIndex : uint ) : VertexBuffer3D
    {
        
        return _geometry.getVertexNormalBuffer( context, contextIndex ) ;
        
    }
    
    
    public function getVertexTangentBuffer( context : Context3D, contextIndex : uint ) : VertexBuffer3D
    {
        
        return _geometry.getVertexTangentBuffer( context, contextIndex ) ;
        
    }
    
    
    public function getIndexBuffer( context : Context3D, contextIndex : uint ) : IndexBuffer3D
    {
        
        return _geometry.getIndexBuffer( context, contextIndex ) ;
        
    }
    
    
    override public function pushModelViewProjection( camera : Camera3D )
    {
        
        var comps   : Vector<Vector3D> ;
        var rot     : Vector3D ;
        if( ++_mvpIndex == _stackLen++ )    _mvpTransformStack[ _mvpIndex ] = new Matrix3D() ;
        var mvp     = _mvpTransformStack[ _mvpIndex ] ;
        mvp.copyFrom( sceneTransform ) ;
        mvp.append( camera.inverseSceneTransform ) ;
        comps       = mvp.decompose() ;
        rot         = comps[1] ;
        rot.x = rot.y = rot.z = 0 ;
        mvp.recompose( comps ) ;
        mvp.append( camera.lens.matrix ) ;
        mvp.copyRowTo( 3, _pos ) ;
        _zIndices[ _mvpIndex ] = -_pos.z ;
        
    }
    
    
    public var numTriangles( get_numTriangles, null ) : Int
    
    
    private function get_numTriangles() : Int
    {
        
        return 2 ;
        
    }
    
    
    public var sourceEntity( get_sourceEntity, null ) : Entity ;
    
    
    private function get_sourceEntity() : Entity
    {
        
        return this ;
        
    }
    
    
    public var material( get_material, set_material ) : MaterialBase ;
    
    
    private function get_material() : MaterialBase
    {
        
        return _material ;
        
    }
    
    
    private function set_material( value : MaterialBase ) : MaterialBase ;
    {
        
        if( value == _material ) return ;
        if( _material ) _material.removeOwner( this ) ;
        _material = value ;
        if( _material ) _material.addOwner( this ) ;
        return _material ;
        
    }
    
    
    public var animation( get_animation, null ) : AnimationBase ;
    
    
    private function get_animation() : AnimationBase
    {
        
        return _nullAnimation ;
        
    }
    
    
    public function animationState( get_animationState, null ) : AnimationStateBase ;
    
    
    private function get_animationState() : AnimationStateBase
    {
        
        return _animationState ;
        
    }
    
    
    public var castsShadows( get_castsShadows, null ) : Bool ;
    
    
    private function get_castsShadows() : Bool
    {
        
        return _shadowCaster ;
        
    }
    
    
    override public function getDefaultBoundingVolume() : BoundingVolumeBase
    {
        
        return new BoundingSphere() ;
        
    }
    
    
    override public function updateBounds()
    {
        
        _bounds.fromExtremes( -.5, -.5, 0, .5, .5, 0 ) ;
        _boundsInvalid = false ;
        
    }
    
    
    override public function createEntityPartitionNode() : EntityNode
    {
        
        return new RenderableNode( this ) ;
        
    }   
    
    override private function updateTransform()
    {
        
        super.updateTransform() ;
        _transform.prependScale( _width, _height, 1 ) ;
        
    }
    
    
}
